from fgs.client.Client import Client
from fgs.client.Bridge import Bridge, BridgeObserver
from fgs.client.Exception_ import FgsException
from fgs.client.IrcClient import IrcClient
from fgs.client.Commands import Commands
from contrib import irclib
import pygtk
pygtk.require ('2.0')
import gtk
import gtk.glade
import gtk.gdk
import gtkhtml2
import pango
import gobject
import os.path
import time
try:
    import threading
except ImportError:
    import dummy_thread as threading

gobject.threads_init()

class GFgsPrettyList(object):
    """Wrapper around the Gtk list objects, to ease the use of lists
    and make use of a beautiful rich list cell."""

    TYPE_FULL = 1
    TYPE_NEWS = 2
    TYPE_IRC = 3

    def __init__(self, treeview, col_name, data, refresh=None, type=TYPE_FULL):

        # Initialize
        self.list = treeview
        self.col_name = col_name
        self.type = type

        # Configure list for type
        self.title = data[0]
        if type == self.TYPE_FULL:
            self.subtitle = data[1]
            self.subsubtitle = data[2]
            self.icon = data[3]
        elif type == self.TYPE_IRC:
            self.icon = data[1]

        self.list_dict = None

        # Configure the view
        self.list.get_selection().set_mode(gtk.SELECTION_SINGLE)

        self.column = gtk.TreeViewColumn(self.col_name)
        self.column.set_expand(True)
        self.list.append_column(self.column)

        # Add special refresh action
        self.refresh_action = refresh
        if refresh:
            i = gtk.Image()
            i.set_from_file('data/refresh.png')
            i.show_all()
            i.set_padding(3, 3)
            c = gtk.TreeViewColumn(self.col_name)
            c.set_widget(i)
            c.set_clickable(True)
            c.connect('clicked', self.refresh_action)
            self.list.append_column(c)


        # Create and pack CellRenderers
        if self.type == self.TYPE_FULL or self.type == self.TYPE_IRC:
            cellicon = gtk.CellRendererPixbuf()
            if self.type == self.TYPE_FULL:
                cellicon.set_property('xpad', 5)
                cellicon.set_property('ypad', 5)
            self.column.pack_start(cellicon, False)
            self.column.set_cell_data_func(cellicon, self.make_icon)

        celltext = gtk.CellRendererText()
        if self.type == self.TYPE_FULL:
            celltext.set_property('xpad', 5)
            celltext.set_property('ypad', 5)
        celltext.set_property('width-chars', 30)
        self.column.pack_start(celltext, True)
        self.column.set_cell_data_func(celltext, self.make_text)

        # Create the backend store
        self.store = gtk.ListStore(int, str, str, str)

        self.list.set_model(self.store)

    # Pixbuf management
    def make_icon(self, column, cell, model, iter):
        from os.path import isfile
        vals = self.get_value_from_id(model.get_value(iter, 0))
        filename = self.get_icon_filename(vals)

        # See if we've already cached the pixbuf data
        if filename and isfile(filename):
            pb = gtk.gdk.pixbuf_new_from_file(filename)
            cell.set_property('pixbuf', pb)
        else:
            cell.set_property('pixbuf', None)

    # Description handler
    def make_text(self, column, cell, model, iter):
        if self.type == self.TYPE_FULL:
            str = '<b>' + model.get_value(iter, 1) + '</b>\n' + \
                  '<span size="smaller">' + model.get_value(iter, 2) + '\n' + \
                  '<tt>' + model.get_value(iter, 3) + '</tt></span>'
        else:
            str = model.get_value(iter, 1)
        cell.set_property('markup', str)

    # Data appender. Receives a dictionary which should contain the
    # values documented in the constructor.
    def set_vals(self, vals_dict, use_dict=True):
        self.clear()
        self.list_dict = vals_dict
        self.use_dict = use_dict
        for k,v in vals_dict.items():
            title = ''
            subtitle = ''
            subsubtitle = ''

            if use_dict:
                v = v.__dict__

            if self.title:
                title = v[self.title]
           
            if self.type == self.TYPE_FULL:
                if self.subtitle:
                    subtitle = v[self.subtitle]
                if self.subsubtitle:
                    subsubtitle = v[self.subsubtitle]

            self.store.append([k, title, subtitle, subsubtitle])

    # Data clearer. Clear the list store.
    def clear(self):
        self.list_dict = None
        self.store.clear()

    # Test whether an item is selected.
    def has_selection(self):
        return self.list.get_selection().get_selected()[1] != None

    # Selection. Return the dictionary content corresponding to the
    # selection.
    def get_selection(self):
        if not self.has_selection():
            return None
        it = self.list.get_selection().get_selected()[1]
        return self.get_value_from_id(self.store.get_value(it, 0))

    def get_value_from_id(self, ident):
        return self.list_dict[ident]

    # Selection ID. Get the ID of the selected item.
    def get_selection_id(self):
        if not self.has_selection():
            return None
        it = self.list.get_selection().get_selected()[1]
        return self.store.get_value(it, 0)

    # Return the filename corresponding to the icon for a particular
    # entry
    def get_icon_filename(self, vals):
        if not self.icon:
            return None
        if self.use_dict:
            fn = vals.__getattr__(self.icon)
        else:
            fn = vals[self.icon]
        if not fn:
            return None
        else:
            return fn

    # Select a portal by uri if exist
    def select(self, uri):
        for id in self.list_dict:
            if self.list_dict[id].url_backend == uri:
                self.list.get_selection().select_path(id)
                return True
        return False

class GFgs(object):

    # Constantes
    TAB_PORTAL = 0
    TAB_NEWS = 1
    TAB_GAMES = 2
    TAB_CHAT = 3
    TAB_SCORES = 4
    
    def __init__(self):

        # Boot the GUI
        filename = os.path.join('data/gfgs.glade')
        self.xml = gtk.glade.XML(filename)
        self.xml.signal_autoconnect(self)

        self.main_window = self.xml.get_widget('main')

        self.portal_list = GFgsPrettyList(self.xml.get_widget('list_portal'), 
                'Community', ['name', 'desc', 'url', 'logo_path'], 
                self.on_refresh_portals)
        self.game_list = GFgsPrettyList(self.xml.get_widget('list_games'), 
                'Game', ['name', 'desc', 'url', 'logo_path'], 
                self.on_refresh_games)
        self.server_list = GFgsPrettyList(self.xml.get_widget('list_servers'),
                'Server', ['fgs', 'version', 'host', None],
                self.on_refresh_servers)
        self.news_list =  GFgsPrettyList(self.xml.get_widget('list_news'),
                'News', ['title'], self.on_refresh_news, GFgsPrettyList.TYPE_NEWS)
        self.xml.get_widget('list_portal').get_selection().connect('changed',
                self.on_list_portal_selection_changed, None)
        self.xml.get_widget('list_games').get_selection().connect('changed',
                self.on_list_games_selection_changed, None)
        self.xml.get_widget('list_servers').get_selection().connect('changed',
                self.on_list_servers_selection_changed, None)
        self.xml.get_widget('list_news').get_selection().connect('changed',
                self.on_list_news_selection_changed, None)

        # Initialize commands
        self.list_store_commands = gtk.ListStore(int, str, str, str)
        lc = self.xml.get_widget('list_commands')
        r = gtk.CellRendererText()
        col = gtk.TreeViewColumn('Name', r, text=1)
        lc.append_column(col)
        col = gtk.TreeViewColumn('Version', r, text=2)
        lc.append_column(col)
        col = gtk.TreeViewColumn('Command', r, text=3)
        lc.append_column(col)
        lc.set_model(self.list_store_commands)

        # Initialize gtkhtml
        self.htmldoc_news = gtkhtml2.Document()
        view = gtkhtml2.View()
        view.set_document(self.htmldoc_news)
        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        sw.add(view)
        sw.show_all()
        self.xml.get_widget('hbox_news').pack_end(sw)

        # Initialize a client
        cache_pth = self._determine_cache_root()
        self.client = Client(cache_root=cache_pth)
        self.bridge = None
        self._busy_cnt = 0
        self._busy_cnt_lck = threading.Lock()
        
        # Initialize irc composant
        self._thread_irc = None
        self._thread_irc_exit = False
        self.irc_prepare()

        self.push_status('Please select a community')
        self._set_portal_connect_status(False)
        
        # Check if client have a config
        if self.client.have_config:
            self.xml.get_widget('portal_login').set_text(self.client.user)
            self.xml.get_widget('portal_pass').set_text(self.client.passwd)
            if self.client.auto_connect:
                self.xml.get_widget('autoconnect_check').set_active(1)
            if not self.portal_list.select(self.client.portal_uri):
                self.xml.get_widget('portal_override_check').set_active(1)
                self.xml.get_widget('portal_uri').set_text(self.client.portal_uri)

        gobject.idle_add(self._fetch_informations)

        gtk.main()

    def _fetch_informations(self):
        self._populate_portal_list(True)
        self.commands = Commands()
        self.lcommands = self.commands.get_by_gid('tetrinet')
        for k in self.lcommands:
            v = self.lcommands[k]
            self.list_store_commands.append([k, v['game_name'], v['game_version'], self.commands.transform(v['game_command'])])


    def _determine_cache_root(self):
        import os, os.path
        # Get ready for some major gruicking.
        home = os.path.expanduser(u'~')
        if os.name == 'nt':
            data_dir = '_fgs'
        else:
            data_dir = '.fgs'
        cache_path = os.path.join(home, data_dir, 'client-cache')

        return cache_path

    def _set_portal_connect_status(self, connected):
        if connected:
            self.xml.get_widget('connect_button').hide()
            self.xml.get_widget('disconnect_button').show()
            self.xml.get_widget('list_portal').set_sensitive(0)
            self.xml.get_widget('portal_override_check').set_sensitive(0)
            self.xml.get_widget('portal_uri').set_sensitive(0)
            self.xml.get_widget('refresh_button').set_sensitive(0)
            self.xml.get_widget('portal_login').set_sensitive(0)
            self.xml.get_widget('portal_pass').set_sensitive(0)
            self.xml.get_widget('login_lbl').set_sensitive(0)
            self.xml.get_widget('password_lbl').set_sensitive(0)

            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_SCORES).show()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_CHAT).show()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_GAMES).show()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_NEWS).show()
            
            self._populate_game_list()
            self._populate_news_list()
            self.server_list.clear()
        else:
            self.xml.get_widget('connect_button').show()
            self.xml.get_widget('disconnect_button').hide()
            self.xml.get_widget('portal_override_check').set_sensitive(1)
            self.xml.get_widget('refresh_button').set_sensitive(1)
            self.on_portal_override_check_toggled(None)
            self.xml.get_widget('portal_login').set_sensitive(1)
            self.xml.get_widget('portal_pass').set_sensitive(1)
            self.xml.get_widget('login_lbl').set_sensitive(1)
            self.xml.get_widget('password_lbl').set_sensitive(1)

            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_SCORES).hide()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_CHAT).hide()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_GAMES).hide()
            self.xml.get_widget('main_tabs').get_nth_page(self.TAB_NEWS).hide()

            self.game_list.clear()
            self.server_list.clear()

            self.replace_status('Select a community to connect to')
            self._thread_irc_exit = True
            if self._thread_irc:
                self._thread_irc.join()

    def push_busy(self):
        self._busy_cnt_lck.acquire()
        self._busy_cnt += 1
        if self._busy_cnt == 1:
            gobject.timeout_add(50, self.update_busy)
        self._busy_cnt_lck.release()

    def pop_busy(self):
        self._busy_cnt_lck.acquire()
        self._busy_cnt -= 1
        self._busy_cnt_lck.release()

    def update_busy(self):
        self.xml.get_widget('busy_bar').pulse()
        if self._busy_cnt_lck.acquire(0):
            if self._busy_cnt == 0:
                self._busy_cnt_lck.release()
                self.xml.get_widget('busy_bar').set_fraction(0.0)
                return False
            self._busy_cnt_lck.release()
        return True

    def push_status(self, text):
        id_ = self.xml.get_widget('status_bar').get_context_id("GFgs")
        self.xml.get_widget('status_bar').push(id_, " " + text)

    def pop_status(self):
        id_ = self.xml.get_widget('status_bar').get_context_id("GFgs")
        self.xml.get_widget('status_bar').pop(id_)

    def replace_status(self, msg):
        self.pop_status()
        self.push_status(msg)

    def _populate_portal_list(self, use_cache=False):
        self.portal_list.set_vals(self.client.portals)

    def _populate_game_list(self):
        if not self.client.portal:
            self.game_list.clear()
        else:
            self.game_list.set_vals(self.client.portal.games)

    def _populate_server_list(self, game):
        l = game.servers
        for k,v in l.items():
            if not v.version.startswith("Running"):
                v.version = "Running version " + v.version
        self.server_list.set_vals(game.servers)

    def _populate_news_list(self):
        n = self.client.portal.news
        self.news_list.set_vals(n.items)

    def on_main_delete_event(self, w, event):
        return False

    def on_main_destroy(self, widget, data=None):
        if self.bridge:
            self.bridge.shutdown_bridge()
        if self._thread_irc:
            self._thread_irc_exit = True
            self._thread_irc.join()
        gtk.main_quit()

    def on_menu_about(self, w, data=None):
        self.xml.get_widget('about').show()

    def gtk_focus(self, name):
        self.xml.get_widget(name).grab_focus()

    def on_main_tabs_switch_page(self, w, e, index):
        if index == self.TAB_PORTAL:
            gobject.idle_add(self.gtk_focus, 'portal_login')
        elif index == self.TAB_CHAT:
            gobject.idle_add(self.gtk_focus, 'irc_message')
            gobject.idle_add(self.irc_newmessage, False)

    def on_refresh_portals(self, w):
        self.client.clear()
        gobject.idle_add(self._populate_portal_list)
        self.replace_status("Community list refreshed from master server")

    def on_refresh_news(self, w):
        gobject.idle_add(self._populate_news_list)

    def on_refresh_games(self, w):
        gobject.idle_add(self._populate_game_list)

    def on_refresh_servers(self, w):
        if self.selected_game:
            gobject.idle_add(self._populate_server_list, self.selected_game)



    #
    # PORTAL TAB
    #
    def on_portal_pass_key_press_event(self, w, e):
        if e.type == gtk.gdk.KEY_PRESS and (e.keyval == 65293 or e.keyval == 65421):
            self.on_connect_button_clicked(w)

    def on_list_portal_selection_changed(self, w, data=None):
        portal = self.portal_list.get_selection()
        if not portal:
            #self.xml.get_widget('portal_desc').set_label("Please select a community in the list.")
            self.xml.get_widget('connect_button').set_sensitive(0)
            return
        #self.xml.get_widget('portal_desc').set_label(portal.long_desc)
        self.xml.get_widget('connect_button').set_sensitive(1)
        gobject.idle_add(self.gtk_focus, 'portal_login')

    def on_connect_button_clicked(self, w):
        override = self.xml.get_widget('portal_override_check').get_active()

        if override:
            portal_name = "(unknown community)"
            portal_uri = self.xml.get_widget('portal_uri').get_text()
            portal_uri_irc = None
        else:
            portal = self.portal_list.get_selection()
            if portal:
                portal_name = portal.name
                portal_uri = portal.url_backend
                portal_uri_irc = portal.url_irc
            else:
                portal_name = None
                portal_uri = None
                portal_uri_irc = None

        user = self.xml.get_widget('portal_login').get_text()
        passwd = self.xml.get_widget('portal_pass').get_text()
        self.user = user

        if not portal_uri:
            self.replace_status("Please select a community!")
            return

        if not (user and passwd):
            self.replace_status("Please enter a login and password!")
            return

        self.replace_status("Connecting to community portal...")

        # save config if autoconnect
        self.client.auto_connect = self.xml.get_widget('autoconnect_check').get_active()
        if self.xml.get_widget('autoconnect_check').get_active():
            self.client.user = user
            self.client.passwd = passwd
            self.client.portal_uri = portal_uri
            self.client.save_config()
            
        try:
            self.client.get_portal(portal_uri, user, passwd)
        except FgsException, e:
            self.error_msg("Login failed! %s" % (e))
            self.replace_status("Select a community to connect to")
            return
        except Exception, e:
            self.error_msg("Connection failed! %s" % (e))
            self.replace_status("Select a community to connect to")
            return

        # switch connect/disconnect button
        self.xml.get_widget('connect_button').hide()
        self.xml.get_widget('disconnect_button').show()

        # create irc thread
        print "chat launch"
        self.portal_uri_irc = portal_uri_irc;
        if (self.portal_uri_irc):
            self._thread_irc_exit = False
            self._thread_irc = threading.Thread(args=(self, user, portal_uri_irc))
            self._thread_irc.run = self._thread_irc_run
            self._thread_irc.setDaemon(True)
            self._thread_irc.start()

        self.replace_status("Connected, populating game list...")
        self._set_portal_connect_status(True)
        self.replace_status("Welcome to %s" % portal_name)

        self.xml.get_widget('main_tabs').set_current_page(2)

    def on_disconnect_button_clicked(self, w):
        self._set_portal_connect_status(False)

    def on_portal_override_check_toggled(self, w):
        override = self.xml.get_widget('portal_override_check').get_active()
        if override:
            self.xml.get_widget('list_portal').set_sensitive(0)
            self.xml.get_widget('refresh_button').set_sensitive(0)
            self.xml.get_widget('portal_uri').set_sensitive(1)
        else:
            self.xml.get_widget('list_portal').set_sensitive(1)
            self.xml.get_widget('refresh_button').set_sensitive(1)
            self.xml.get_widget('portal_uri').set_sensitive(0)

    def on_refresh_button_clicked(self, w):
        self.on_refresh_portals(w)

    #
    # GAME COMMAND 
    #

    def command_show(self):
        self.xml.get_widget('game_location').show()

    def command_hide(self, stop_cmd=True):
        if stop_cmd:
            self.commands.stop()
        self.xml.get_widget('game_location').hide()

    def on_command_cancel_clicked(self, w):
        self.command_hide()

    def on_command_ok_clicked(self, w):
        iter = self.xml.get_widget('list_commands').get_selection().get_selected()[1]
        if iter == None:
            return
        cmd = self.list_store_commands.get_value(iter, 3)
        self.commands.start(cmd)

    #
    # NEWS TAB
    #
    def on_list_news_selection_changed(self, w, data=None):
        item = self.news_list.get_selection()
        self.htmldoc_news.open_stream('text/html')
        if not item:
            self.htmldoc_news.write_stream('<html></html>')
        else:
            self.htmldoc_news.write_stream(item.data)
        self.htmldoc_news.close_stream()

    def htmlize(self, data):
        return '<html><body>'+data+'</body></html>'

    #
    # CHAT TAB
    #
    def irc_prepare(self):
        self.irc_nicks = []
        
        w = self.xml.get_widget('irc_textview')
        w.modify_base(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0, 0))
        buf = w.get_buffer()
        self.irc_mark = buf.create_mark('End', buf.get_end_iter(), False)
        
        self.irc_style_system = gtk.TextTag('system')
        self.irc_style_system.set_property('foreground', 'gray')
        self.irc_style_system.set_property('font', 'courier')
        buf.get_tag_table().add(self.irc_style_system)
        
        self.irc_style_message = gtk.TextTag('message')
        self.irc_style_message.set_property('foreground', 'white')
        self.irc_style_message.set_property('font', 'courier')
        buf.get_tag_table().add(self.irc_style_message)

        self.irc_style_action = gtk.TextTag('action')
        self.irc_style_action.set_property('foreground', 'blue')
        self.irc_style_action.set_property('font', 'courier')
        buf.get_tag_table().add(self.irc_style_action)

        self.irc_style_priv_message = gtk.TextTag('privmessage')
        self.irc_style_priv_message.set_property('foreground', 'white')
        self.irc_style_priv_message.set_property('style', pango.STYLE_ITALIC)
        self.irc_style_priv_message.set_property('font', 'courier')
        buf.get_tag_table().add(self.irc_style_priv_message)

        self.list_irc_nicks =  GFgsPrettyList(self.xml.get_widget('list_irc_nicks'),
                'Players', ['nick', 'icon'], None, GFgsPrettyList.TYPE_IRC)

    def irc_update_nicklist(self):
        self.irc_nicks = self._irc_client.nicklist
        nicks = {}
        for nick in self.irc_nicks:
            icon = ''
            if nick[0] == '@':
                nick = '<span foreground="#000088" weight="bold">' + nick[1:] + '</span>'
                icon = 'data/irc-admin.png'
            nicks[len(nicks)] = {'nick': nick, 'icon': icon}
        self.list_irc_nicks.set_vals(nicks, False)

    def on_irc_message_backspace(self, w, e):
        if e.type == gtk.gdk.KEY_PRESS:
            if e.keyval == 65293 or e.keyval == 65421: # return
                self._irc_client.send(w.get_text())
                w.set_text('')
                return True
            elif e.keyval == 65289: # tab (autocompletion)
                text = w.get_text()
                s = text.split(' ')
                last = s[len(s)-1]
                if len(last) > 1:
                    for nick in self.irc_nicks:
                        if nick[0] == '@':
                            nick = nick[1:]
                        if nick.startswith(last):
                            text = text.rstrip(last) + nick + ', '
                            w.set_text(text)
                            w.set_position(len(text))
                            break
                return True

    def irc_add_message(self, msg, tag = None, nl = True):
        w = self.xml.get_widget('irc_textview')
        buf = w.get_buffer()
        (start, end) = buf.get_bounds()
        if tag:
            buf.insert_with_tags(end, msg, tag)
        else:
            buf.insert(end, msg)
        if nl:
            buf.insert(end, "\n")
        w.scroll_to_mark(self.irc_mark, 0, True, 1, 1)
        if tag == self.irc_style_message: 
            self.irc_newmessage()

    def irc_clean(self):
        self.list_irc_nicks.clear()
        self.irc_topic('')

    def irc_topic(self, topic):
        self.xml.get_widget('irc_topic').set_text(topic)

    def irc_newmessage(self, havenew=True):
        n = self.xml.get_widget('main_tabs').get_current_page()
        if havenew and n != self.TAB_CHAT:
            self.xml.get_widget('lbl_chat').set_markup('<b>Chat *</b>')
        else:
            self.xml.get_widget('lbl_chat').set_markup('Chat')

    def _colorize_nick(self, nick):
        colors = ['ff0000', '00ff00', '0000ff', '00ffff', 'ffff00', 'ff00ff']
        c = colors[(id(nick)+len(nick)) % len(colors)]
        return c
        
    def _thread_irc_handle_event(self, event, args):
        if event == "connect":
            gobject.idle_add(self.irc_clean)
            gobject.idle_add(self.irc_add_message, "* Connecting to %s" % (args), self.irc_style_system)
        elif event == "disconnect":
            gobject.idle_add(self.irc_add_message, "* Disconnect from %s" % (args), self.irc_style_system)
            gobject.idle_add(self.irc_clean)
        elif event == "topic":
            gobject.idle_add(self.irc_topic, args[0])
        elif event == "pubmsg":
            gobject.idle_add(self.irc_add_message, args[0], self.irc_style_message, False)
            gobject.idle_add(self.irc_add_message, "> %s" % (args[1]), self.irc_style_message)
        elif event == "privmsg":
            gobject.idle_add(self.irc_add_message, args[0], self.irc_style_message, False)
            gobject.idle_add(self.irc_add_message, "> %s" % (args[1]), self.irc_style_priv_message)
        elif event == "join":
            gobject.idle_add(self.irc_add_message, "* %s join the channel %s" % (args[0], args[1]), self.irc_style_system)
            gobject.idle_add(self.irc_update_nicklist)
        elif event == "part":
            gobject.idle_add(self.irc_add_message, "* %s have left the channel" % (args[0]), self.irc_style_system)
            gobject.idle_add(self.irc_update_nicklist)
        elif event == "quit":
            gobject.idle_add(self.irc_add_message, "* %s has quit" % (args[0]), self.irc_style_system)
            gobject.idle_add(self.irc_update_nicklist)
        elif event == "action":
            gobject.idle_add(self.irc_add_message, "* %s %s" % (args[0], args[1]), self.irc_style_action)
        elif event == "nick":
            gobject.idle_add(self.irc_add_message, "* %s is now known as %s" % (args[0], args[1]), self.irc_style_system)
            gobject.idle_add(self.irc_update_nicklist)
        elif event == "namreply":
            gobject.idle_add(self.irc_update_nicklist)


    def _thread_irc_run(self):
        self._irc_client = IrcClient(self.user, self.portal_uri_irc)
        self._irc_client.add_dispatcher(self._thread_irc_handle_event)
        try:
            self._irc_client.connect()
            while not self._thread_irc_exit:
                time.sleep(.1)
                self._irc_client.process()
        except irclib.ServerConnectionError:
            gobject.idle_add(self.irc_add_message, "* Server is down", self.irc_style_system)
            self.xml.get_widget('main_tabs').get_nth_page(3).hide()

        self._irc_client.disconnect()

    #
    # GAME TAB
    #
    def on_list_games_selection_changed(self, w, data=None):
        game = self.game_list.get_selection()
        self.selected_game = game
        if not game:
            self.server_list.clear()
            return
        self.xml.get_widget('game_start').set_sensitive(0)
        self._populate_server_list(game)

    def on_list_servers_selection_changed(self, w, data=None):
        self.server = self.server_list.get_selection()
        if not self.server:
            self.xml.get_widget('game_start').set_sensitive(0)
            return
        self.xml.get_widget('game_start').set_sensitive(1)

    def on_game_start_clicked(self, w, data=None):
        if not self.bridge:
            self.xml.get_widget('game_start').set_sensitive(0)
            self.bridge = Bridge(self.server)
            self.set_bridged_status()
            self.bridge_obs = GFgsBridgeObs(self.bridge, self)
            try:
                self.bridge.start_bridge()
                self.command_show()
            except FgsException, e:
                self.bridge = None
                self.bridge_obs = None
                self.xml.get_widget('bridge').hide()
                self.error_msg("%s" % e)

    def on_game_stop_clicked(self, w, data=None):
        if self.bridge:
            self.xml.get_widget('game_stop').set_sensitive(0)
            self.bridge_shutdown()

    #
    # ERROR DIALOG
    #
    def error_msg(self, msg):
        self.xml.get_widget('error_lbl').set_label(unicode(msg, errors='replace'))
        self.xml.get_widget('error').show()

    def on_error_ok_clicked(self, w):
        self.xml.get_widget('error').hide()

    #
    # BRIDGE FRAME
    #
    def update_status(self, text):
        self.xml.get_widget('status_txt').set_label(text)

    def update_xmit_count(self, size, is_downstream):
        if is_downstream:
            self.in_cnt += size
            self.xml.get_widget('in_cnt').set_label("In:\n%s" % self.in_cnt)
        else:
            self.out_cnt += size
            self.xml.get_widget('out_cnt').set_label("Out:\n%s" % self.out_cnt)

    def set_idle_status(self):
        self.pop_busy()
        self.in_cnt = 0
        self.out_cnt = 0
        self.xml.get_widget('game_start').show()
        self.xml.get_widget('game_start').set_sensitive(1)
        self.xml.get_widget('game_stop').hide()
        self.xml.get_widget('list_games').set_sensitive(1)
        self.xml.get_widget('list_servers').set_sensitive(1)
        self.xml.get_widget('status_txt').set_label("No game running.")
        self.xml.get_widget('txmit_data').set_label("")
        self.xml.get_widget('in_cnt').set_label("")
        self.xml.get_widget('out_cnt').set_label("")

    def set_bridged_status(self):
        self.push_busy()
        self.in_cnt = 0
        self.out_cnt = 0
        self.xml.get_widget('game_start').hide()
        self.xml.get_widget('game_stop').show()
        self.xml.get_widget('game_stop').set_sensitive(1)
        self.xml.get_widget('list_games').set_sensitive(0)
        self.xml.get_widget('list_servers').set_sensitive(0)
        self.xml.get_widget('txmit_data').set_label("Transmitted data")
        self.xml.get_widget('in_cnt').set_label("In:\n0")
        self.xml.get_widget('out_cnt').set_label("Out:\n0")

    def bridge_shutdown(self):
        self.set_idle_status()
        if self.bridge:
            self.bridge.shutdown_bridge()
        self.command_hide(True)
        del self.bridge
        self.bridge = None
        del self.bridge_obs

    def bridge_error(self, exc):
        self.bridge_shutdown()
        self.error_msg("Game link failed: %s" % exc)

class GFgsBridgeObs(BridgeObserver):
    def __init__(self, bridge, fgs):
        self.fgs = fgs
        BridgeObserver.__init__(self, bridge)

    def init_client(self):
        gobject.idle_add(self.fgs.update_status, "Connecting to game server")

    def identify_client(self):
        gobject.idle_add(self.fgs.update_status, "Authenticating to server")

    def init_server(self):
        gobject.idle_add(self.fgs.update_status, "Setting up local relay")

    def wait_for_gamer(self):
        gobject.idle_add(self.fgs.update_status, "Waiting for client connection\n" +
                                                        "Connect to localhost:%s" % self.fgs.bridge.localport)

    def bridge_active(self):
        gobject.idle_add(self.fgs.update_status, "Link established!")

    def send_data(self, data, is_downstream):
        gobject.idle_add(self.fgs.update_xmit_count, len(data), is_downstream)

    def bridge_error(self, exc):
        gobject.idle_add(self.fgs.bridge_error, exc)

    def bridge_shutdown(self):
        gobject.idle_add(self.fgs.bridge_shutdown)
